#!/usr/bin/python
# -*- coding: UTF-8 -*-
from __future__ import print_function
import math
import threading
import time
import numpy as np
import smbus
# from adxl345 import ADXL345
# 导入ADXL345
import socket
import urllib.request as ur
import urllib.parse
import RPi.GPIO as GPIO
from distlib.compat import raw_input
from scipy.signal import find_peaks
from datetime import datetime
import sys
import L76X
# import pyautogui
import signal

'''
强制结束线程
'''


def _async_raise(tid):
    """raises the exception, performs cleanup if needed"""
    tid.cancle()


'''
加速度模块类
'''

revision = ([l[12:-1] for l in open('/proc/cpuinfo', 'r').readlines() if l[:8] == "Revision"] + ['0000'])[0]
bus = smbus.SMBus(1 if int(revision, 16) >= 4 else 0)

EARTH_GRAVITY_MS2 = 9.80665
SCALE_MULTIPLIER = 0.004

DATA_FORMAT = 0x31
BW_RATE = 0x2C
POWER_CTL = 0x2D

BW_RATE_1600HZ = 0x0F
BW_RATE_800HZ = 0x0E
BW_RATE_400HZ = 0x0D
BW_RATE_200HZ = 0x0C
BW_RATE_100HZ = 0x0B
BW_RATE_50HZ = 0x0A
BW_RATE_25HZ = 0x09

RANGE_2G = 0x00
RANGE_4G = 0x01
RANGE_8G = 0x02
RANGE_16G = 0x03

MEASURE = 0x08
AXES_DATA = 0x32


class ADXL345:
    address = None

    # address=0x53 i2c地址

    def __init__(self, address=0x53):
        self.address = address
        self.set_bandwidth_rate(BW_RATE_100HZ)
        self.set_range(RANGE_2G)
        self.enable_measurement()

    def enable_measurement(self):
        bus.write_byte_data(self.address, POWER_CTL, MEASURE)

    def set_bandwidth_rate(self, rate_flag):
        bus.write_byte_data(self.address, BW_RATE, rate_flag)

    def set_range(self, range_flag):
        value = bus.read_byte_data(self.address, DATA_FORMAT)

        value &= ~0x0F
        value |= range_flag
        value |= 0x08

        bus.write_byte_data(self.address, DATA_FORMAT, value)

    def get_axes(self, gforce=False):
        bytes = bus.read_i2c_block_data(self.address, AXES_DATA, 6)

        x = bytes[0] | (bytes[1] << 8)
        if x & (1 << 16 - 1):
            x = x - (1 << 16)

        y = bytes[2] | (bytes[3] << 8)
        if y & (1 << 16 - 1):
            y = y - (1 << 16)

        z = bytes[4] | (bytes[5] << 8)
        if z & (1 << 16 - 1):
            z = z - (1 << 16)

        x = x * SCALE_MULTIPLIER
        y = y * SCALE_MULTIPLIER
        z = z * SCALE_MULTIPLIER

        if gforce is False:
            x = x * EARTH_GRAVITY_MS2
            y = y * EARTH_GRAVITY_MS2
            z = z * EARTH_GRAVITY_MS2

        x = round(x, 4)
        y = round(y, 4)
        z = round(z, 4)

        return {"x": x, "y": y, "z": z}


# 加速度传感器
adxl345 = ADXL345()
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_client_socket.connect(("434b2006a8.qicp.vip", 49916))
# axes={'x':'123','y':'15615','z':'4562'}
pitch_low = 0
pitch_high = 0
# xyz传感器数据池（x,y,z）原始数据
xyz_data = [[], [], []]
# 心率传感器数据池（red,ir）原始数据
heat_o2_natual_data = [[], []]
# 心率数据（处理后数据）
heat_o2_data = [[], []]


class RepeatingTimer(threading.Timer):
    # 定时器
    def run(self):
        while not self.finished.is_set():
            self.function(*self.args, **self.kwargs)
            self.finished.wait(self.interval)


'''
加速度传感器
'''


# 加速度传感器数据处理
def get_prp():
    axes = adxl345.get_axes(True)
    x = axes['x']
    y = axes['y']
    z = axes['z']
    # 数据处理转换
    pitch = math.degrees(np.arctan(x / np.sqrt(y * y + z * z)))
    roll = math.degrees(np.arctan(y / np.sqrt(x * x + z * z)))
    paw = math.degrees(np.arctan(np.sqrt(x * x + z * z) / x))
    return float(pitch), float(roll), float(paw)


# 加速度传感器数据获取
def get_xyz_func():
    pitch, roll, paw = get_prp()
    print("ADXL345 on address 0x%x:" % (adxl345.address))
    print("俯视角：", pitch)
    print("左右偏侧角：", roll)
    print("竖直方向转角：{}\n".format(paw))

    # 判断跌倒（x轴）
    try:
        threadLock.acquire()
        if pitch < float(-70) or pitch > float(55):
            i = 0
            pitch_arr = []
            while (i < 60):
                i = i + 1
                pitch2, roll, paw = get_prp()
                pitch_arr.append(pitch2)
            lenth = len(pitch_arr)
            num = np.sum(i > float(-80) or i < float(60) for i in pitch_arr)
            if num > (lenth / 3 * 2):
                print("跌倒！！！")
                tcp_client_socket.send("xyz:*跌倒\n".encode("utf-8"))
        # 向服务器发送数据
        else:
            tcp_client_socket.send("xyz:*无碍\n".encode("utf-8"))
            # tcp_client_socket.send((str(pitch) + "*" + str(roll) + "*" + str(paw) + "\n").encode("utf-8"))
        # print(type(str(pitch)))
    except:
        print("云端连接失败！运动状态上传失败！\n")
    threadLock.release()
    pass


threadLock2 = threading.Lock()


# 三轴加速度线程
class xyz_thread(threading.Thread):
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name

    def run(self):
        print("Starting " + self.name)
        # 获得锁，成功获得锁定后返回True
        # 可选的timeout参数不填时将一直阻塞直到获得锁定
        # 否则超时后将返回False
        threadLock2.acquire()
        xyz_func()
        # 释放锁
        threadLock2.release()


def xyz_func():
    # 开启定时器，每两秒获取数据
    t_xyz = RepeatingTimer(3.0, get_xyz_func)
    t_xyz.start()


'''
心率血氧传感器
'''
'''
1、血氧模块类
'''
I2C_WRITE_ADDR = 0xAE
I2C_READ_ADDR = 0xAF

REG_INIR_STATUS_1 = 0x00
REG_INIR_STATUS_2 = 0x01

REG_INIR_ENABLE_1 = 0x02
REG_INIR_ENABLE_2 = 0x03

REG_FIFO_WR_PTR = 0x04
REG_OVF_COUNTER = 0x05
REG_FIFO_RD_PTR = 0x06
REG_FIFO_DATA = 0x07
REG_FIFO_CONFIG = 0x08

REG_MODE_CONFIG = 0x09
REG_SPO2_CONFIG = 0x0A
REG_LED1_PA = 0x0C

REG_LED2_PA = 0x0D
REG_PILOT_PA = 0x10
REG_MULTI_LED_CTRL1 = 0x11
REG_MULTI_LED_CTRL2 = 0x12

REG_TEMP_INTR = 0x1F
REG_TEMP_FRAC = 0x20
REG_TEMP_CONFIG = 0x21
REG_PROX_INT_THRESH = 0x30
REG_REV_ID = 0xFE
REG_PART_ID = 0xFF

MAX_BRIGHTNESS = 255


# Max30102定义类
class MAX30102():
    def __init__(self, channel=1, address=0X57, gpio_pin=4):
        print("Channel:{0}, address:{1}".format(channel, address))
        self.address = address
        self.channel = channel
        self.bus = smbus.SMBus(self.channel)
        self.interrupt = gpio_pin

        # set gpio mode
        # GPIO.setmode(GPIO.BOARD)
        GPIO.setup(self.interrupt, GPIO.IN)

        self.reset()

        time.sleep(1)

        reg_data = self.bus.read_i2c_block_data(self.address, REG_INIR_STATUS_1, 1)
        self.setup()

    def shutdown(self):
        self.bus.write_i2c_block_data(self.address, REG_MODE_CONFIG, [0x80])

    def reset(self):
        self.bus.write_i2c_block_data(self.address, REG_MODE_CONFIG, [0x40])

    def setup(self, led_mode=0x03):
        self.bus.write_i2c_block_data(self.address, REG_INIR_ENABLE_1, [0xc0])
        self.bus.write_i2c_block_data(self.address, REG_INIR_ENABLE_2, [0x00])
        self.bus.write_i2c_block_data(self.address, REG_FIFO_WR_PTR, [0x00])
        self.bus.write_i2c_block_data(self.address, REG_OVF_COUNTER, [0x00])
        self.bus.write_i2c_block_data(self.address, REG_FIFO_RD_PTR, [0x00])

        self.bus.write_i2c_block_data(self.address, REG_FIFO_CONFIG, [0x4f])
        self.bus.write_i2c_block_data(self.address, REG_MODE_CONFIG, [led_mode])
        self.bus.write_i2c_block_data(self.address, REG_SPO2_CONFIG, [0x27])
        self.bus.write_i2c_block_data(self.address, REG_LED1_PA, [0x24])
        self.bus.write_i2c_block_data(self.address, REG_LED2_PA, [0x24])
        self.bus.write_i2c_block_data(self.address, REG_PILOT_PA, [0x7f])

    def set_config(self, reg, value):
        self.bus.write_i2c_block_data(self.address, reg, value)

    def read_fifo(self):
        red_led = []
        ir_led = []

        reg_INTR1 = self.bus.read_i2c_block_data(self.address, REG_INIR_STATUS_1, 1)
        reg_INTR2 = self.bus.read_i2c_block_data(self.address, REG_INIR_STATUS_2, 1)
        d = []
        d = self.bus.read_i2c_block_data(self.address, REG_FIFO_DATA, 6)
        if d is None:
            red_led = None
            ir_led = None
        else:
            red_led = (d[0] << 16 | d[1] << 8 | d[2]) & 0x03FFFF
            ir_led = (d[3] << 16 | d[4] << 8 | d[5]) & 0x03FFFF
        return red_led, ir_led

    def read_sequential(self, amount=100):
        red_buf = []
        ir_buf = []
        Spo2 = []
        num = 0
        for i in range(amount):
            while GPIO.input(self.interrupt) == 1:
                pass
            red, ir = self.read_fifo()
            '''
            ###
            '''
            R = red / (ir + red)
            data = 110 - 25 * R
            num = num + data
            Spo2.append(data)
            red_buf.append(red)
            ir_buf.append(ir)
        return red_buf, ir_buf, Spo2, num


'''
血氧模块线程
'''


# 血氧定时线程
class heat_o2_thread_1(threading.Thread):
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self._stop_event = threading.Event()

    def run(self):
        print("Starting " + self.name)
        # 定时测血氧
        heat_o2_func(self.name)

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()


def heat_o2_func(threadName):
    # 开启定时器，每1小时获取血氧数据
    time_heat_o2 = RepeatingTimer(3600, get_heat_o2_natual_func)
    m = "00"
    while True:
        now = datetime.now()  # current date and time
        time_data = now.strftime("%H:%M:%S")
        time_data_list = time_data.split(":")
        if time_data_list[1] != m:
            print(time_data)
            m = time_data_list[1]
        if time_data == "20:00:00":
            time_heat_o2.start()
        elif time_data == "12:00:00":
            time_heat_o2.cancel()


def get_heat_o2_natual_func():
    # 读取血氧初始数据
    m = MAX30102()
    # 获取数据长度
    red, ir, spo2, num = m.read_sequential(1000)
    data_red_na = red
    data_ir_na = ir


class Telnet(object):
    def send(self, cmd, timeout):
        self.telnetObj.write(cmd.encode('utf8'))


# 血氧主动测试
class heat_o2_thread_2(threading.Thread):
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self._stop_event = threading.Event()

    def run(self):
        while True:
            c = raw_input()
            if c == "q":
                print("退出...")
                sys.exit()
                # pyautogui.hotkey('ctrl', 'z')
                # thread_heat_o2_01.stop()
                # thread_heat_o2_02.stop()
                # exit(1)
                # _async_raise(thread_heat_o2_02, SystemExit)
                # _async_raise(thread_heat_o2_02, SystemExit)
                # _async_raise(thread_xyz, SystemExit)
            elif c == "w":
                print("检测中...")
                print("请耐心等待30s")
                get_heat_o2_func()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()


threadLock_heat = threading.Lock()


def get_heat_o2_func():
    # 读取血氧初始数据
        m = MAX30102()
        # 获取数据长度
        red, ir, spo2, num = m.read_sequential(1000)

        data_red = red
        data_ir = ir
        data_spo2 = num / len(spo2)
        data_h = np.fft.fft(data_red, n=None, axis=-1, norm=None)
        [pks, locs] = find_peaks(data_h, height=None, threshold=None, distance=5,
                                 prominence=None, width=None, wlen=None, rel_height=None,
                                 plateau_size=None)
        heat_data = len(pks) / 2
        o2_data = round(data_spo2, 2)
        # if heat_data < float(50) | heat_data > float(200):
        #     print("心率测试失败！请重试！")
        # else:
        print("心率为:每分钟{}下".format(heat_data))
        # if o2_data < float(90) | o2_data > float(100):
        #     print("血氧测试失败！请重试！")
        # else:
        print("血氧浓度为：{}%".format(o2_data))
        try:
            threadLock_heat.acquire()
            tcp_client_socket.send(("heat+o2:*" + (str(len(pks) / 2) + "*" + str(data_spo2))+"\n").encode("utf-8"))
            threadLock_heat.release()
        except:
            print("云端连接失败！血氧心跳数据上传失败！请重试！\n")


'''
GPS模块进程
'''


def GPS_init():
    """
    GPS初始化
    :return:
    """
    x = L76X.L76X()
    x.L76X_Set_Baudrate(9600)
    x.L76X_Send_Command(x.SET_NMEA_BAUDRATE_115200)
    # time.sleep(2)
    x.L76X_Set_Baudrate(115200)

    x.L76X_Send_Command(x.SET_POS_FIX_400MS)

    # Set output message
    x.L76X_Send_Command(x.SET_NMEA_OUTPUT)

    x.L76X_Exit_BackupMode()
    return x


class GPS_thread(threading.Thread):
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name

    def run(self):
        print("Starting " + self.name)
        # 获得锁，成功获得锁定后返回True
        # 可选的timeout参数不填时将一直阻塞直到获得锁定
        # 否则超时后将返回False
        GPS_func()
        # 释放锁


def GPS_func():
    # 开启定时器，每5秒获取数据
    t_GPS = RepeatingTimer(5.0, get_GPS_func)
    t_GPS.start()


def get_GPS_func():
    x_GPS = GPS_init()
    data2 = x_GPS.L76X_Gat_GNRMC()
    data2 = str(data2, 'utf-8')
    try:
        print("位置信息:", data2)
        print("\n")
        index = data2.find('$GNRMC')
        st = data2[index:]
        st = st.split('$')
        print("GNRMC的位置信息", st[1])
        try:
            if x_GPS.Status == 1:
                print('接收到位置信息！')
                tcp_client_socket.send((str("GPS:*S*" + st[1]+"\n").encode("utf-8")))
            else:
                print('定位失败，请重试！')
                tcp_client_socket.send((str("GPS:*F*" + "失败"+"\n").encode("utf-8")))
        except:
            print("\n获取位置信息失败")
            tcp_client_socket.send((str("GPS:*F*" + "失败"+"\n").encode("utf-8")))
    except:
        print("\n获取位置信息失败")
        tcp_client_socket.send((str("GPS:*F*" + "失败"+"\n").encode("utf-8")))


# def send_func():


# 线程锁
threadLock = threading.Lock()
threads = []

# 创建新线程
thread_xyz = xyz_thread(1, "Thread-xyz")
thread_heat_o2_01 = heat_o2_thread_1(2, "Thread-heat_o2_timer")
thread_heat_o2_02 = heat_o2_thread_2(3, "Thread-heat_o2_writer")
thread_GPS = GPS_thread(4, "Thread-GPS")

# 开启新线程
thread_xyz.start()
thread_heat_o2_01.start()
thread_heat_o2_02.start()
thread_GPS.start()

# 添加线程到线程列表s
threads.append(thread_xyz)
threads.append(thread_heat_o2_01)
threads.append(thread_heat_o2_02)
threads.append(thread_GPS)

# 等待所有线程完成
for t in threads:
    t.join()
print("手环报废")
